home *** CD-ROM | disk | FTP | other *** search
/ EuroCD 3 / EuroCD 3.iso / Graphics_Utils / CyberGraphX / CyberAnim / src / CyberAnim.c next >
Encoding:
C/C++ Source or Header  |  1998-06-24  |  68.2 KB  |  2,172 lines

  1. /*****************************************************************************
  2.  
  3.         Cybergraphics Animation Viewer v1.2          1 Jun 1997
  4.         -----------------------------------
  5.  
  6. The files in this archive may be distributed anywhere provided they are
  7. unmodified and are not sold for profit.
  8.  
  9. Ownership and copyright of all files remains with the author:
  10.  
  11.     Peter McGavin, 86 Totara Crescent, Lower Hutt, New Zealand.
  12.     e-mail: peterm@maths.grace.cri.nz
  13.  
  14. *****************************************************************************/
  15.  
  16. #include "CyberAnim.h"  /* separate file for GST */
  17.  
  18. #include "unpack.h"
  19. #include "math64.h"
  20.  
  21. #define NBITMAPS   2
  22.  
  23. /****************************************************************************/
  24.  
  25. #ifdef __SASC
  26. const char version[] = "$VER: CyberAnim 1.2 " __AMIGADATE__ ;
  27. long __oslibversion = 38;    /* we require at least OS3.0 */
  28. char __stdiowin[] = "CON:20/50/500/130/CyberAnim";
  29. char __stdiov37[] = "/AUTO/CLOSE";
  30. #endif
  31.  
  32. #ifndef max
  33. #define max(x,y) ((x)>=(y))?(x):(y)
  34. #endif
  35.  
  36. char programname[20] = "CyberAnim";
  37. BPTR olddir = NULL;
  38. struct RDArgs *rdargs = NULL;
  39.  
  40. /****************************************************************************/
  41.  
  42. #define ID_ANIM MAKE_ID('A','N','I','M')
  43. #define ID_ILBM MAKE_ID('I','L','B','M')
  44. #define ID_ANHD MAKE_ID('A','N','H','D')
  45. #define ID_BMHD MAKE_ID('B','M','H','D')
  46. #define ID_CMAP MAKE_ID('C','M','A','P')
  47. #define ID_CAMG MAKE_ID('C','A','M','G')
  48. #define ID_BODY MAKE_ID('B','O','D','Y')
  49. #define ID_DLTA MAKE_ID('D','L','T','A')
  50.  
  51. /*  Masking techniques    */
  52. #define    mskNone            0
  53. #define    mskHasMask        1
  54. #define    mskHasTransparentColor    2
  55. #define    mskLasso        3
  56. #define    mskHasAlpha        4
  57.  
  58. /*  Compression techniques  */
  59. #define    cmpNone            0
  60. #define    cmpByteRun1        1
  61. #define    cmpByteRun2        2
  62.  
  63. /*  Bitmap header (BMHD) structure  */
  64. struct BitMapHeader
  65. {
  66.   UWORD bmh_Width;        /* Width in pixels */
  67.   UWORD bmh_Height;        /* Height in pixels */
  68.   WORD  bmh_Left;        /* Left position */
  69.   WORD  bmh_Top;        /* Top position */
  70.   UBYTE bmh_Depth;        /* Number of planes */
  71.   UBYTE bmh_Masking;        /* Masking type */
  72.   UBYTE bmh_Compression;    /* Compression type */
  73.   UBYTE bmh_Pad;
  74.   UWORD bmh_Transparent;    /* Transparent color */
  75.   UBYTE bmh_XAspect;
  76.   UBYTE bmh_YAspect;
  77.   WORD  bmh_PageWidth;
  78.   WORD  bmh_PageHeight;
  79. };
  80.  
  81. /*  Animation compression modes  */
  82. #define cmpDirect        0
  83. #define cmpXor            1
  84. #define cmpLongdelta        2
  85. #define cmpShortdelta        3
  86. #define cmpDelta        4
  87. #define cmpBytedelta        5
  88. #define cmpStereo        6
  89. #define cmpAnim7        7
  90. #define cmpAnim8        8
  91. #define cmpJ            74
  92.  
  93. /*  Animation header flags  */
  94. #define anfLongdata    (1<<0)
  95. #define anfXor        (1<<1)
  96. #define anfOnelist    (1<<2)
  97. #define anfRLC        (1<<3)
  98. #define anfVertical    (1<<4)
  99. #define anfLongoffsets    (1<<5)
  100.  
  101. /*  Animation header (ANHD) structure  */
  102. struct AnimHeader
  103. {
  104.   UBYTE anh_Operation;        /* Compression method */
  105.   UBYTE anh_Mask;        /* plane mask (xor mode only) */
  106.   UWORD anh_W, anh_H;        /* w & h of body (xor mode only) */
  107.   WORD  anh_X, anh_Y;        /* offset of body (xor mode only) */
  108.   ULONG anh_Abstime;        /* 1/60s sec relative to 1st frame */
  109.   ULONG anh_Reltime;        /* 1/60s sec relative to prev frame */
  110.   UBYTE anh_Interleave;        /* Modify frame this many back (0=2) */
  111.   UBYTE anh_Pad0;
  112.   ULONG anh_Bits;        /* animation header flags */
  113.   UBYTE anh_Pad[16];
  114. };
  115.  
  116. struct mystream {
  117.   FILE *f;
  118.   UBYTE *rambuf;
  119.   UBYTE *rambufptr;
  120.   ULONG rambufsize;
  121. };
  122.  
  123. struct options {
  124.   BOOL ram;
  125.   BOOL once;
  126.   BOOL dbuf;
  127.   BOOL warp;
  128.   BOOL modereq;
  129.   BOOL waittof;
  130.   BOOL waitbovp;
  131.   BOOL changescreenbuffer;
  132.   BOOL loopframes;
  133.   ULONG mode;
  134. };
  135.  
  136. /****************************************************************************/
  137. /* resources (must be freed on exit) */
  138.  
  139. struct Library *CyberGfxBase = NULL;
  140.  
  141. static struct Screen *s = NULL;
  142. static struct Window *w = NULL;
  143.  
  144. static struct ScreenBuffer *sb[2] = {NULL, NULL};
  145. static struct MsgPort *safeport = NULL;
  146. static struct MsgPort *dispport = NULL;
  147. static BOOL safe = TRUE;
  148. static BOOL disp = TRUE;
  149. static BOOL waited_until_safe = FALSE;
  150. static BOOL using_changescreenbuffer = FALSE;
  151. static BOOL using_waittof = FALSE;
  152. static BOOL using_waitbovp = FALSE;
  153.  
  154. static struct BitMap *bm[NBITMAPS] = {NBITMAPS * NULL};
  155. static BOOL using_fastmem_bitmap = FALSE;
  156. static BOOL using_intermediate_buffer = FALSE;
  157. static WORD *dirty = NULL;  /* min and max row changed for each 32 pixel col */
  158.  
  159. static struct IFFHandle *iff = NULL;
  160. static BOOL iff_is_open = FALSE;
  161.  
  162. static UWORD *emptypointer = NULL;
  163.  
  164. static struct FileRequester *fr = NULL;
  165. static struct ScreenModeRequester *smr = NULL;
  166.  
  167. struct Library *TimerBase = NULL;
  168. static struct MsgPort *timermp = NULL;
  169. static struct timerequest *timerio = NULL;
  170. static ULONG timerclosed = TRUE;
  171. static struct EClockVal *time = NULL;
  172. static struct EClockVal *time0 = NULL;
  173. static struct EClockVal *time1 = NULL;
  174. static double micros_per_eclock; /* Length of EClock tick in microseconds */
  175.  
  176. static struct MsgPort *timermp2 = NULL;
  177. static struct timerequest *timerio2 = NULL;
  178. static ULONG timer2closed = TRUE;
  179.  
  180. /****************************************************************************/
  181.  
  182. static void wait_until_safe (void)
  183. {
  184.   ULONG sig;
  185.  
  186.   if (!waited_until_safe) {
  187.     /* Wait until beam has finished displayable bitmap for bm[].
  188.        Need to wait for cybergraphics TOF here, but HOW????
  189.        WaitTOF() appears to wait for custom chips which are different
  190.        frequency and phase to cybergraphics display.
  191.        Use 1/10 second timeout because sometimes changescreenbuffer msgs
  192.        aren't replied to, e.g, when overscan HAM anim is dragged right down.
  193.     */
  194.     if (using_changescreenbuffer) {
  195.       if (!safe) {
  196.         timerio2->tr_node.io_Command = TR_ADDREQUEST;
  197.         timerio2->tr_time.tv_secs = 0;
  198.         timerio2->tr_time.tv_micro = 100000;  /* 1/10 of a second */
  199.         SendIO ((struct IORequest *)timerio2);
  200.         sig = Wait ((1 << safeport->mp_SigBit) | (1 << timermp2->mp_SigBit));
  201.         if ((sig & (1 << timermp2->mp_SigBit)) == 0) {
  202.           AbortIO ((struct IORequest *)timerio2);
  203.           Wait (1 << timermp2->mp_SigBit);
  204.         } else
  205.           printf ("Warning: ChangeScreenBuffer() succeeded but SafeMessage wasn't replied to!\n");
  206.         while (GetMsg (safeport) != NULL) /* clear message queue */
  207.           /* do nothing */ ;
  208.         while (GetMsg (timermp2) != NULL) /* clear message queue */
  209.           /* do nothing */ ;
  210.         safe = TRUE;
  211.       }
  212.     } else if (using_waittof)
  213.       WaitTOF ();
  214.     else if (using_waitbovp)
  215.       WaitBOVP (&s->ViewPort);
  216.     waited_until_safe = TRUE;
  217.   }
  218. }
  219.  
  220. /****************************************************************************/
  221.  
  222. static void wait_until_disp (void)
  223. /* Wait until it is safe to swap bitmaps without flicker */
  224. /* Use 1/10 second timeout because sometimes msgs aren't replied to! */
  225. {
  226.   ULONG sig;
  227.  
  228.   if (using_changescreenbuffer) {
  229.     if (!disp) {
  230.       timerio2->tr_node.io_Command = TR_ADDREQUEST;
  231.       timerio2->tr_time.tv_secs = 0;
  232.       timerio2->tr_time.tv_micro = 100000;  /* 1/10 of a second */
  233.       SendIO ((struct IORequest *)timerio2);
  234.       sig = Wait ((1 << dispport->mp_SigBit) | (1 << timermp2->mp_SigBit));
  235.       if ((sig & (1 << timermp2->mp_SigBit)) == 0) {
  236.         AbortIO ((struct IORequest *)timerio2);
  237.         Wait (1 << timermp2->mp_SigBit);
  238.       } else
  239.         printf ("Warning: ChangeScreenBuffer() succeeded but DispMessage wasn't replied to!\n");
  240.       while (GetMsg (dispport) != NULL) /* clear message queue */
  241.         /* do nothing */ ;
  242.       while (GetMsg (timermp2) != NULL) /* clear message queue */
  243.         /* do nothing */ ;
  244.       disp = TRUE;
  245.     }
  246.   }
  247. }
  248.  
  249. /****************************************************************************/
  250.  
  251. static void partial_cleanup (void)
  252. /* free resources allocated by animate_file() */
  253. {
  254.   int i, plane;
  255.  
  256.   if (using_changescreenbuffer) {
  257.     wait_until_safe ();
  258.     wait_until_disp ();
  259.   }
  260.   if (iff_is_open) {
  261.     CloseIFF (iff);
  262.     iff_is_open = FALSE;
  263.   }
  264.   if (iff != NULL) {
  265.     if (iff->iff_Stream != NULL) {
  266.       if (((struct mystream *)iff->iff_Stream)->f != NULL)
  267.         fclose (((struct mystream *)iff->iff_Stream)->f);
  268.       if (((struct mystream *)iff->iff_Stream)->rambuf != NULL)
  269.         free (((struct mystream *)iff->iff_Stream)->rambuf);
  270.       free ((void *)iff->iff_Stream);
  271.     }
  272.     FreeIFF (iff);
  273.     iff = NULL;
  274.   }
  275.   for (i = 0; i < NBITMAPS; i++) {
  276.     if (!using_intermediate_buffer) {
  277.       if (bm[i] != NULL)
  278.         if (!using_changescreenbuffer)
  279.           free (bm[i]);
  280.     } else if (using_fastmem_bitmap) {
  281.       if (bm[i] != NULL) {
  282.         for (plane = 0; plane < bm[i]->Depth; plane++)
  283.           if (bm[i]->Planes[plane] != NULL)
  284.             free (bm[i]->Planes[plane]);
  285.         free (bm[i]);
  286.       }
  287.     } else if (bm[i] != NULL)
  288.       FreeBitMap (bm[i]);
  289.     bm[i] = NULL;
  290.   }
  291.   if (dirty != NULL) {
  292.     free (dirty);
  293.     dirty = NULL;
  294.   }
  295.   if (w != NULL) {
  296.     CloseWindow (w);
  297.     w = NULL;
  298.   }
  299.   if (sb[1] != NULL) {
  300.     FreeScreenBuffer (s, sb[1]);
  301.     sb[1] = NULL;
  302.   }
  303.   if (sb[0] != NULL) {
  304.     FreeScreenBuffer (s, sb[0]);
  305.     sb[0] = NULL;
  306.   }
  307.   if (s != NULL) {
  308.     CloseScreen (s);
  309.     s = NULL;
  310.   }
  311.   if (dispport != NULL) {
  312.     DeletePort (dispport);
  313.     dispport = NULL;
  314.   }
  315.   if (safeport != NULL) {
  316.     DeletePort (safeport);
  317.     safeport = NULL;
  318.   }
  319.   if (emptypointer != NULL) {
  320.     FreeMem (emptypointer, 12);
  321.     emptypointer = NULL;
  322.   }
  323. }
  324.  
  325. /****************************************************************************/
  326.  
  327. static void cleanup (void)
  328. /* free all resources */
  329. {
  330.   partial_cleanup ();
  331.   if (IFFParseBase != NULL) {
  332.     CloseLibrary ((struct Library *)IFFParseBase);
  333.     IFFParseBase = NULL;
  334.   }
  335.   if (olddir != NULL) {
  336.     CurrentDir (olddir);
  337.     olddir = NULL;
  338.   }
  339.   if (rdargs != NULL) {
  340.     FreeArgs (rdargs);
  341.     rdargs = NULL;
  342.   }
  343.   if (time1 != NULL) {
  344.     FreeMem (time1, sizeof(struct EClockVal));
  345.     time1 = NULL;
  346.   }
  347.   if (time0 != NULL) {
  348.     FreeMem (time0, sizeof(struct EClockVal));
  349.     time0 = NULL;
  350.   }
  351.   if (time != NULL) {
  352.     FreeMem (time, sizeof(struct EClockVal));
  353.     time = NULL;
  354.   }
  355.   if (!timer2closed) {
  356.     if (!CheckIO((struct IORequest *)timerio2)) {
  357.       AbortIO ((struct IORequest *)timerio2);
  358.       WaitIO ((struct IORequest *)timerio2);
  359.     }
  360.     CloseDevice ((struct IORequest *)timerio2);
  361.     timer2closed = TRUE;
  362.   }
  363.   if (timerio2 != NULL) {
  364.     DeleteExtIO ((struct IORequest *)timerio2);
  365.     timerio2 = NULL;
  366.   }
  367.   if (timermp2 != NULL) {
  368.     DeletePort (timermp2);
  369.     timermp2 = NULL;
  370.   }
  371.   if (!timerclosed) {
  372.     if (!CheckIO((struct IORequest *)timerio)) {
  373.       AbortIO ((struct IORequest *)timerio);
  374.       WaitIO ((struct IORequest *)timerio);
  375.     }
  376.     CloseDevice ((struct IORequest *)timerio);
  377.     timerclosed = TRUE;
  378.     TimerBase = NULL;
  379.   }
  380.   if (timerio != NULL) {
  381.     DeleteExtIO ((struct IORequest *)timerio);
  382.     timerio = NULL;
  383.   }
  384.   if (timermp != NULL) {
  385.     DeletePort (timermp);
  386.     timermp = NULL;
  387.   }
  388.   if (smr != NULL) {
  389.     FreeAslRequest (smr);
  390.     smr = NULL;
  391.   }
  392.   if (fr != NULL) {
  393.     FreeAslRequest (fr);
  394.     fr = NULL;
  395.   }
  396.   if (AslBase != NULL) {
  397.     CloseLibrary ((struct Library *)AslBase);
  398.     AslBase = NULL;
  399.   }
  400.   if (CyberGfxBase != NULL) {
  401.     CloseLibrary ((struct Library *)CyberGfxBase);
  402.     CyberGfxBase = NULL;
  403.   }
  404. }
  405.  
  406. /****************************************************************************/
  407. /* CTRL/C handler */
  408.  
  409. #ifdef __SASC
  410. void _CXBRK (void)
  411. {
  412.   cleanup ();
  413.   fprintf (stderr, "**Break\n");
  414.   exit (0);
  415. }
  416. #endif
  417.  
  418. /****************************************************************************/
  419. /* Error handlers */
  420.  
  421. static char bodystring[64];
  422.  
  423. static struct TextAttr topaz80 = {
  424.   "topaz.font", 8, 0, 0
  425. };
  426.  
  427. static struct IntuiText bodytext[] = {
  428.   {0, 1, JAM2, 10, 8, &topaz80, programname, &bodytext[1]},
  429.   {0, 1, JAM2, 10, 20, &topaz80, bodystring, NULL},
  430. };
  431.  
  432. static struct IntuiText negtext = {0, 1, JAM2, 6, 3, &topaz80, "Ok", NULL};
  433.  
  434. static jmp_buf err_jmp;
  435.  
  436. static void abort_anim (char *msg, ...)
  437. /* Exit animate_file() with message */
  438. {
  439.   va_list arglist;
  440.  
  441.   va_start (arglist, msg);
  442.   vsprintf (bodystring, msg, arglist);
  443.   va_end (arglist);
  444.   AutoRequest (w, &bodytext[0], NULL, &negtext, 0, 0, 320, 60);
  445.   partial_cleanup ();    /* free resources allocated by animate_file() */
  446.   longjmp (err_jmp, 1);  /* return from animate_file() */
  447. }
  448.  
  449. static void die (char *msg, ...)
  450. /* Exit program with message, return code 10 */
  451. {
  452.   va_list arglist;
  453.  
  454.   va_start (arglist, msg);
  455.   vsprintf (bodystring, msg, arglist);
  456.   va_end (arglist);
  457.   AutoRequest (w, &bodytext[0], NULL, &negtext, 0, 0, 320, 60);
  458.   cleanup ();
  459.   exit (10);
  460. }
  461.  
  462. /****************************************************************************/
  463. /* safe memory allocator */
  464.  
  465. static void *malloc_check (size_t size)
  466. {
  467.   void *p;
  468.  
  469.   if ((p = malloc (size)) == NULL)
  470.     die ("Out of memory trying to allocate %ld bytes!", size);
  471.   return (p);
  472. }
  473.  
  474. /****************************************************************************/
  475.  
  476. static void delay_until (struct EClockVal *next_time)
  477. /* delay until next_time or next intuievent or CTRL/C */
  478. /* (in the latter cases, abort the timer delay) */
  479. {
  480.   ULONG sig;
  481.  
  482.   ReadEClock (time);
  483.   if (cmp64 (time, next_time) > 0) {
  484.     timerio->tr_node.io_Command = TR_ADDREQUEST;
  485.     *(struct EClockVal *)&timerio->tr_time = *next_time;
  486.     sub64 ((struct EClockVal *)&timerio->tr_time, time); /* timerio->tr_time -= time */
  487.     BeginIO ((struct IORequest *)timerio);    /* begin delay */
  488.     sig = Wait ((1 << timermp->mp_SigBit) |
  489.                 (1 << w->UserPort->mp_SigBit) | /* also return for IDCMP */
  490.                 SIGBREAKF_CTRL_C);              /* or SIGBREAKF_CTRL_C */
  491.     if ((sig & (1 << timermp->mp_SigBit)) == 0) {
  492.       SetSignal (sig, sig);  /* restore IDCMP and SIGBREAKF_CTRL_C signals */
  493.       AbortIO ((struct IORequest *)timerio);
  494.       WaitIO ((struct IORequest *)timerio);
  495.     }
  496.     while (GetMsg (timermp) != NULL)
  497.       /* nothing */ ;
  498.   }
  499.   ReadEClock (next_time);
  500. }
  501.  
  502. /****************************************************************************/
  503.  
  504. static char *mode_name (ULONG mode)
  505. /* Return a mode name for given mode, compatible with ASL mode requester */
  506. /* Use name in DisplayInfo database if available */
  507. /* else manually construct the name */
  508. {
  509.   APTR handle;
  510.   char *p;
  511.   struct NameInfo nameinfo;
  512.   struct DimensionInfo dimsinfo;
  513.   static char modename[DISPLAYNAMELEN + 4 + 4 + 11 + 1];
  514.  
  515.   p = modename;
  516.   *p = '\0';
  517.   if ((handle = FindDisplayInfo (mode & ~SPRITES)) != NULL) {
  518.     if (GetDisplayInfoData (handle, (UBYTE *)&nameinfo,
  519.                             sizeof(nameinfo), DTAG_NAME,
  520.                             NULL) > sizeof(struct QueryHeader)) {
  521.       p += sprintf (p, "%s", nameinfo.Name);
  522.     } else if (GetDisplayInfoData (handle, (UBYTE *)&dimsinfo,
  523.                                    sizeof(dimsinfo), DTAG_DIMS,
  524.                                    NULL) >= 66 /* sizeof(dimsinfo)*/) {
  525.       switch (mode & MONITOR_ID_MASK) {
  526.         case DEFAULT_MONITOR_ID:
  527.           p += sprintf (p, "DEFAULT:");      /* PAL or NTSC??? */
  528.           break;
  529.         case NTSC_MONITOR_ID:
  530.           p += sprintf (p, "NTSC:");
  531.           break;
  532.         case PAL_MONITOR_ID:
  533.           p += sprintf (p, "PAL:");
  534.           break;
  535.         case VGA_MONITOR_ID:
  536.           p += sprintf (p, "MULTISCAN:");
  537.           break;
  538.         case A2024_MONITOR_ID:
  539.           p += sprintf (p, "A2024:");
  540.           break;
  541.         case PROTO_MONITOR_ID:
  542.           p += sprintf (p, "PROTO:");
  543.           break;
  544.         case EURO72_MONITOR_ID:
  545.           p += sprintf (p, "EURO72:");
  546.           break;
  547.         case EURO36_MONITOR_ID:
  548.           p += sprintf (p, "EURO36:");
  549.           break;
  550.         case SUPER72_MONITOR_ID:
  551.           p += sprintf (p, "SUPER72:");
  552.           break;
  553.         case DBLNTSC_MONITOR_ID:
  554.           p += sprintf (p, "DBLNTSC:");
  555.           break;
  556.         case DBLPAL_MONITOR_ID:
  557.           p += sprintf (p, "DBLPAL:");
  558.           break;
  559.         default:
  560.           p += sprintf (p, "(unknown):");
  561.           break;
  562.       }
  563.       p += sprintf (p, "%d x %d",
  564.                     dimsinfo.Nominal.MaxX - dimsinfo.Nominal.MinX + 1,
  565.                     dimsinfo.Nominal.MaxY - dimsinfo.Nominal.MinY + 1);
  566.       if ((mode & HAM_KEY) != 0)
  567.         p += sprintf (p, " HAM");
  568.       if ((mode & EXTRAHALFBRITE_KEY) != 0)
  569.         p += sprintf (p, " EHB");
  570.       if ((mode & LACE) != 0)
  571.         p += sprintf (p, " Interlaced");
  572.     } else {
  573.       p += sprintf (p, "%s", "(unnamed)");
  574.       if ((mode & HAM_KEY) != 0)
  575.         p += sprintf (p, " HAM");
  576.       if ((mode & EXTRAHALFBRITE_KEY) != 0)
  577.         p += sprintf (p, " EHB");
  578.       if ((mode & LACE) != 0)
  579.         p += sprintf (p, " Interlaced");
  580.     }
  581.   } else {
  582.     p += sprintf (p, "%s", "(unavailable)");
  583.   }
  584.   return (modename);
  585. }
  586.  
  587. /****************************************************************************/
  588.  
  589. static ULONG parse_mode (char *modename)
  590. /* Modename may be descriptive name ("PAL Lores"), or hex ("$00420001" or */
  591. /* "0x12345678") or decimal ("32768"). */
  592. {
  593.   ULONG mode, reason;
  594.  
  595.   mode = INVALID_ID;
  596.   if (modename != NULL) {
  597.     if (modename[0] == '0' && modename[1] == 'x') {
  598.       if (sscanf (&modename[2], "%lx", &mode) != 1)
  599.         mode = INVALID_ID;
  600.     } else if (modename[0] == '$') {
  601.       if (sscanf (&modename[1], "%lx", &mode) != 1)
  602.         mode = INVALID_ID;
  603.     } else if (modename[0] >= '0' && modename[0] <= '9') {
  604.       if (sscanf (modename, "%ld", &mode) != 1)
  605.         mode = INVALID_ID;
  606.     } else {
  607.       while ((mode = NextDisplayInfo (mode)) != INVALID_ID) {
  608.         if ((mode & LORESDPF_KEY) == 0) {
  609.           /* printf ("$%08x  \"%s\"\n", mode, mode_name (mode)); */
  610.           if (strcmp (mode_name (mode), modename) == 0)
  611.             break;
  612.         }
  613.       }
  614.     }
  615.   }
  616.   if (FindDisplayInfo (mode) == NULL)
  617.     die ("ScreenMode not in database: \"%s\"", modename);
  618.   if ((reason = ModeNotAvailable (mode)) != 0)
  619.     die ("Mode $%08x is not available: %ld", mode, reason);
  620.   return (mode);
  621. }
  622.  
  623. /****************************************************************************/
  624.  
  625. static void parse_tooltypes (char *fname, struct options *opt)
  626. /* set options according to icon tooltypes */
  627. {
  628.   struct DiskObject *obj;
  629.   char **toolarray, *s;
  630.  
  631.   if ((obj = GetDiskObject (fname)) != NULL) {
  632.     toolarray = obj->do_ToolTypes;
  633.     if (FindToolType (toolarray, "DISK") != NULL)
  634.       opt->ram = FALSE;
  635.     if (FindToolType (toolarray, "RAM") != NULL)
  636.       opt->ram = FALSE;
  637.     if (FindToolType (toolarray, "ONCE") != NULL)
  638.       opt->once = TRUE;
  639.     if (FindToolType (toolarray, "WARP") != NULL)
  640.       opt->warp = TRUE;
  641.     if (FindToolType (toolarray, "NOMODEREQ") != NULL)
  642.       opt->modereq = FALSE;
  643.     if (FindToolType (toolarray, "WAITTOF") != NULL)
  644.       opt->waittof = TRUE;
  645.     if (FindToolType (toolarray, "WAITBOVP") != NULL)
  646.       opt->waitbovp = TRUE;
  647.     if (FindToolType (toolarray, "CHANGESCREENBUFFER") != NULL)
  648.       opt->changescreenbuffer = TRUE;
  649.     if (FindToolType (toolarray, "LOOPFRAMES") != NULL)
  650.       opt->loopframes = FALSE;
  651.     if ((s = FindToolType (toolarray, "SCREENMODE")) != NULL)
  652.       opt->mode = parse_mode (s);
  653.     FreeDiskObject (obj);
  654.   }
  655. }
  656.  
  657. /****************************************************************************/
  658.  
  659. static ULONG *calc_cmap (struct Screen *s, UBYTE *cmap, int ncolours,
  660.                          ULONG intended_mode)
  661. /* cmap is in ILBM format */
  662. /* Allocate and return structure in format for LoadRGB32() */
  663. {
  664.   int c;
  665.   ULONG *colourtable;
  666.  
  667.   colourtable = (ULONG *)malloc_check ((1 + 3 * ncolours + 1) * sizeof(ULONG));
  668.   colourtable[0] = (ncolours << 16) + 0;
  669.   for (c = 0; c < ncolours; c++) {
  670.     colourtable[3 * c + 1] = *cmap++ * 0x01010101;
  671.     colourtable[3 * c + 2] = *cmap++ * 0x01010101;
  672.     colourtable[3 * c + 3] = *cmap++ * 0x01010101;
  673.   }
  674.   if ((intended_mode & EXTRAHALFBRITE_KEY) != 0) {
  675.     cmap -= 3 * ncolours;
  676.     for (c = ncolours >> 1; c < ncolours; c++) {
  677.       colourtable[3 * c + 1] = (cmap[0] >> 1) * 0x01010101;
  678.       colourtable[3 * c + 2] = (cmap[1] >> 1) * 0x01010101;
  679.       colourtable[3 * c + 3] = (cmap[2] >> 1) * 0x01010101;
  680.       cmap += 3;
  681.     }
  682.   }
  683.   colourtable[1 + 3 * ncolours] = 0;
  684.   return (colourtable);
  685. }
  686.  
  687. /****************************************************************************/
  688.  
  689. static BOOL unpackrow (BYTE **src0, BYTE *dst, WORD dstbytes)
  690. {
  691.   BYTE *src;
  692.   BYTE n;
  693.   BOOL err;
  694.  
  695.   err = TRUE;
  696.   src = *src0;
  697.   while (dstbytes > 0) {
  698.     n = *src++;
  699.     if (n >= 0) {
  700.       n += 1;
  701.       if ((dstbytes -= n) < 0)
  702.         goto error;
  703.       memcpy (dst, src, n);
  704.       src += n;
  705.     } else if (n != -128) {
  706.       n = 1 - n;
  707.       if ((dstbytes -= n) < 0)
  708.         goto error;
  709.       memset (dst, *src++, n);
  710.     }
  711.     dst += n;
  712.   }
  713.   err = FALSE;
  714. error:
  715.   *src0 = src;
  716.   return (err);
  717. }
  718.  
  719. /****************************************************************************/
  720.  
  721. static void unpacklongdelta (WORD *wdata, LONG *plane, WORD bytesperrow,
  722.                              WORD *dirty)
  723. {
  724.   WORD offset;
  725.   UWORD count;
  726.  
  727.   while ((offset = *wdata++) != -1) {
  728.     if (offset >= 0) {
  729.       plane += offset;
  730.       *plane = *(LONG *)wdata;
  731.       wdata += 2;
  732.     } else {
  733.       count = *(UWORD *)wdata;
  734.       wdata++;
  735.       plane += -offset - 1;
  736.       memcpy (plane, wdata, count << 2);
  737.       plane += count - 1;
  738.       wdata += (count << 1);
  739.     }
  740.   }
  741. }
  742.  
  743. /****************************************************************************/
  744.  
  745. static void unpackshortdelta (WORD *wdata, WORD *plane, WORD bytesperrow,
  746.                               WORD *dirty)
  747. {
  748.   WORD offset;
  749.   UWORD count;
  750.  
  751.   while ((offset = *wdata++) != -1) {
  752.     if (offset >= 0) {
  753.       plane += offset;
  754.       *plane = *wdata++;
  755.     } else {
  756. /*
  757.       plane += -offset - 1;
  758.       for (count = *wdata++; count > 0; count--)
  759.         *plane++ = *wdata++;
  760.       plane--;
  761. */
  762.       count = *(UWORD *)wdata;
  763.       wdata++;
  764.       plane += -offset - 1;
  765.       memcpy (plane, wdata, count << 1);
  766.       plane += count - 1;
  767.       wdata += count;
  768.     }
  769.   }
  770. }
  771.  
  772. /****************************************************************************/
  773.  
  774. #if 0
  775. void __asm unpackbytedelta (register __a0 BYTE *bdata,
  776.                             register __a1 PLANEPTR plane,
  777.                             register __d0 WORD bytesperrow,
  778.                             register __a2 WORD *dirty);
  779.  
  780. static void unpackbytedelta (BYTE *bdata, PLANEPTR plane, WORD bytesperrow,
  781.                              WORD *dirty)
  782. {
  783.   WORD x, y, count, w, first, last;
  784.   BYTE *bp;
  785.   UBYTE ub;
  786.  
  787.   for (x = 0; x < bytesperrow; x++) {
  788.     first = -1;
  789.     last = -1;
  790.     bp = &plane[x];
  791.     y = 0;
  792.     for (count = *bdata++; count > 0; count--) {
  793.       if ((w = *bdata++) > 0) {
  794.         y += w;
  795.         bp += w * bytesperrow;
  796.       } else if (w < 0) {
  797.         if (first < 0)
  798.           first = y;
  799.         last = (y += (w &= 0x7f));
  800.         for ( ; w > 0; w--) {
  801.           *bp = *bdata++;
  802.           bp += bytesperrow;
  803.         }
  804.       } else /* w == 0 */ {
  805.         if (first < 0)
  806.           first = y;
  807.         last = (y += (ub = (UBYTE)*bdata++));
  808.         w = *bdata++;
  809.         for ( ; ub > 0; ub--) {
  810.           *bp = w;
  811.           bp += bytesperrow;
  812.         }
  813.       }
  814.     }
  815.     if (first >= 0 && (dirty[0] < 0 || first < dirty[0]))
  816.       dirty[0] = first;
  817.     if (last >= 0 && (dirty[1] < 0 || last > dirty[1]))
  818.       dirty[1] = last;
  819.     if ((x & 3) == 3)
  820.       dirty += 2;
  821.   }
  822. }
  823. #endif
  824.  
  825. /****************************************************************************/
  826.  
  827. #if 0
  828. static void unpackanim7word (BYTE *bdata, WORD *data, PLANEPTR plane,
  829.                              WORD wordsperrow, WORD *dirty)
  830. {
  831.   WORD x, y, count, w, first, last;
  832.   WORD *wp;
  833.   UBYTE ub;
  834.  
  835.   for (x = 0; x < wordsperrow; x++) {
  836.     first = -1;
  837.     last = -1;
  838.     wp = &((WORD *)plane)[x];
  839.     y = 0;
  840.     for (count = *bdata++; count > 0; count--) {
  841.       if ((w = *bdata++) > 0) {
  842.         y += w;
  843.         wp += w * wordsperrow;
  844.       } else if (w < 0) {
  845.         if (first < 0)
  846.           first = y;
  847.         last = (y += (w &= 0x7f));
  848.         for ( ; w > 0; w--) {
  849.           *wp = *data++;
  850.           wp += wordsperrow;
  851.         }
  852.       } else /* w == 0 */ {
  853.         if (first < 0)
  854.           first = y;
  855.         last = (y += (ub = (UBYTE)*bdata++));
  856.         w = *data++;
  857.         for ( ; ub > 0; ub--) {
  858.           *wp = w;
  859.           wp += wordsperrow;
  860.         }
  861.       }
  862.     }
  863.     if (first >= 0 && (dirty[0] < 0 || first < dirty[0]))
  864.       dirty[0] = first;
  865.     if (last >= 0 && (dirty[1] < 0 || last > dirty[1]))
  866.       dirty[1] = last;
  867.     if ((x & 1) == 1)
  868.       dirty += 2;
  869.   }
  870. }
  871. #endif
  872.  
  873. /****************************************************************************/
  874.  
  875. #if 0
  876. void __asm unpackanim7long (register __a0 BYTE *bdata,
  877.                             register __a4 LONG *data,
  878.                             register __a1 PLANEPTR plane,
  879.                             register __d0 WORD bytesperrow,
  880.                             register __a2 WORD *dirty);
  881.  
  882. static void unpackanim7long (BYTE *bdata, LONG *data, PLANEPTR plane,
  883.                              WORD bytesperrow, WORD *dirty)
  884. {
  885.   WORD x, y, count, w, first, last;
  886.   LONG *lp, l;
  887.   UBYTE ub;
  888.  
  889.   for (x = 0; x < bytesperrow; x += 4) {
  890.     first = -1;
  891.     last = -1;
  892.     lp = &((LONG *)plane)[x];
  893.     y = 0;
  894.     for (count = *bdata++; count > 0; count--) {
  895.       if ((w = *bdata++) > 0) {
  896.         y += w;
  897.         lp += w * (bytesperrow >> 2);
  898.       } else if (w < 0) {
  899.         if (first < 0)
  900.           first = y;
  901.         last = (y += (w &= 0x7f));
  902.         for ( ; w > 0; w--) {
  903.           *lp = *data++;
  904.           lp += (bytesperrow >> 2);
  905.         }
  906.       } else /* w == 0 */ {
  907.         if (first < 0)
  908.           first = y;
  909.         last = (y += (ub = (UBYTE)*bdata++));
  910.         l = *data++;
  911.         for ( ; ub > 0; ub--) {
  912.           *lp = l;
  913.           lp += (bytesperrow >> 2);
  914.         }
  915.       }
  916.     }
  917.     if (first >= 0 && (dirty[0] < 0 || first < dirty[0]))
  918.       dirty[0] = first;
  919.     if (last >= 0 && (dirty[1] < 0 || last > dirty[1]))
  920.       dirty[1] = last;
  921.     dirty += 2;
  922.   }
  923. }
  924. #endif
  925.  
  926. /****************************************************************************/
  927.  
  928. static void blit_opt (struct BitMap *bm, struct RastPort *rp, WORD *dirty,
  929.                       int yoffset)
  930. /* Blit from bm to rp starting from yoffset.
  931.    Try to minimise amount of data blitted using information in dirty.
  932.    Conversely, try to reduce the number of blits.
  933.    dirty contains min and max rows changed for each 32-pixel-wide column
  934.    (or (-1,-1) for unchanged columns).
  935. */
  936. {
  937.   WORD x, firstx, miny, maxy;
  938.  
  939.   firstx = -1;
  940.   for (x = 0; x < bm->BytesPerRow; x += 4) {
  941.     if (dirty[0] >= 0 && dirty[1] >= 0) {
  942.       if (firstx == -1) {
  943.         firstx = x;
  944.         miny = dirty[0];
  945.         maxy = dirty[1];
  946.       } else if (abs(dirty[0] - miny) < 10 &&
  947.                  abs(dirty[1] - maxy) < 10) {
  948.         miny = min(dirty[0], miny);
  949.         maxy = max(dirty[1], maxy);
  950.       } else {
  951.         BltBitMapRastPort (bm, firstx << 3, miny, rp, firstx << 3,
  952.                            miny + yoffset, (x - firstx) << 3,
  953.                            maxy - miny, 0xc0);
  954.         firstx = x;
  955.         miny = dirty[0];
  956.         maxy = dirty[1];
  957.       }
  958.     } else if (firstx != -1) {
  959.       BltBitMapRastPort (bm, firstx << 3, miny, rp, firstx << 3,
  960.                          miny + yoffset, (x - firstx) << 3,
  961.                          maxy - miny, 0xc0);
  962.       firstx = -1;
  963.     }
  964.     dirty += 2;
  965.   }
  966.   if (firstx != -1)
  967.     BltBitMapRastPort (bm, firstx << 3, miny, rp, firstx << 3,
  968.                        miny + yoffset, (bm->BytesPerRow - firstx) << 3,
  969.                        maxy - miny, 0xc0);
  970. }
  971.  
  972. /****************************************************************************/
  973.  
  974. #if 0
  975. static void PrintTopChunk (struct IFFHandle *iff)
  976. {
  977.   struct ContextNode *top;
  978.   short i;
  979.   char idbuf[5];
  980.  
  981.   /* Get a pointer to the context node describing the current context. */
  982.   if (!(top = CurrentChunk (iff)))
  983.     return;
  984.  
  985.   /*
  986.    * Print a series of dots equivalent to the current nesting depth of chunks processed so far.
  987.    * This will cause nested chunks to be printed out indented.
  988.    */
  989.   for (i = iff->iff_Depth;  i--; )
  990.     printf (". ");
  991.  
  992.   /* Print out the current chunk's ID and size. */
  993.   printf ("%s %ld ", IDtoStr (top->cn_ID, idbuf), top->cn_Size);
  994.  
  995.   /* Print the current chunk's type, with a newline. */
  996.   puts (IDtoStr (top->cn_Type, idbuf));
  997. }
  998. #endif
  999.  
  1000. /****************************************************************************/
  1001. /* stream handler for iffparse allows reading from disk or ram */
  1002.  
  1003. #ifdef __SASC
  1004. static __saveds __asm LONG mystreamhandler (
  1005.   register __a0 struct Hook *hook,
  1006.   register __a2 struct IFFHandle *iff,
  1007.   register __a1 struct IFFStreamCmd *actionpkt)
  1008. {
  1009. #else /* gcc */
  1010. static LONG mystreamhandler (void)
  1011. {
  1012.   register struct Hook *hook __asm("a0");
  1013.   register struct IFFHandle *iff __asm("a2");
  1014.   register struct IFFStreamCmd *actionpkt __asm("a1");
  1015. #endif
  1016.   struct mystream *s;
  1017.   LONG nbytes, error;
  1018.   UBYTE *buf;
  1019.  
  1020.   s = (struct mystream *)iff->iff_Stream;
  1021.   nbytes = actionpkt->sc_NBytes;
  1022.   buf = (UBYTE *)actionpkt->sc_Buf;
  1023.   switch (actionpkt->sc_Command) {
  1024.     case IFFCMD_READ:
  1025.       if (s->f != NULL)
  1026.         error = (fread (buf, 1, nbytes, s->f) != nbytes);
  1027.       else if (s->rambuf != NULL &&
  1028.                nbytes <= s->rambuf + s->rambufsize - s->rambufptr &&
  1029.                nbytes >= 0) {
  1030.         /* printf ("Reading %d from %d\n", nbytes, s->rambufptr - s->rambuf); */
  1031.         if (nbytes > 0) {
  1032.           memcpy (buf, s->rambufptr, nbytes);
  1033.           s->rambufptr += nbytes;
  1034.         }
  1035.         error = FALSE;
  1036.       } else
  1037.         error = TRUE;
  1038.       break;
  1039.     case IFFCMD_WRITE:
  1040.       if (s->f != NULL)
  1041.         error = (fwrite (buf, 1, nbytes, s->f) != nbytes);
  1042.       else
  1043.         error = TRUE;
  1044.       break;
  1045.     case IFFCMD_SEEK:
  1046.       if (s->f != NULL)
  1047.         error = (fseek (s->f, nbytes, SEEK_CUR) == -1);
  1048.       else if (s->rambuf != NULL) {
  1049.         s->rambufptr += nbytes;
  1050.         error = s->rambufptr > (s->rambuf + s->rambufsize) ||
  1051.                 s->rambufptr < s->rambuf;
  1052.       } else
  1053.         error = TRUE;
  1054.       break;
  1055.     case IFFCMD_INIT:
  1056.     case IFFCMD_CLEANUP:
  1057.       error = FALSE;
  1058.       break;
  1059.     default:
  1060.       error = TRUE;
  1061.   }
  1062.   return (error);
  1063. }
  1064.  
  1065. static struct Hook mystreamhook = {
  1066.   {NULL},
  1067.   (ULONG (*)())mystreamhandler,
  1068.   NULL,
  1069.   NULL
  1070. };
  1071.  
  1072. /****************************************************************************/
  1073.  
  1074. static ULONG __saveds __asm smr_filter (register __a0 struct Hook *smr_filter_hook,
  1075.                                         register __a2 struct ScreenModeRequester *smr,
  1076.                                         register __a1 ULONG mode)
  1077. /* reject modes deeper than depth 8 */
  1078. /* (because setting ASLSM_MaxDepth = 8 seems to be insufficient) */
  1079. {
  1080.   UWORD count;
  1081.   struct DimensionInfo dimsinfo;
  1082.   void *handle;
  1083.  
  1084.   if ((handle = FindDisplayInfo (mode)) != NULL &&
  1085.       (count = GetDisplayInfoData (handle, (UBYTE *)&dimsinfo,
  1086.                                    sizeof(struct DimensionInfo), DTAG_DIMS,
  1087.                                    NULL)) < 66
  1088.                                              /* sizeof(struct DimensionInfo) */)
  1089.     abort_anim ("GetDisplayInfoData(Dims) failed (%d)", count);
  1090.   return (ULONG)(dimsinfo.MaxDepth <= 8);
  1091. }
  1092.  
  1093. /****************************************************************************/
  1094.  
  1095. static void wait_for_click (void)
  1096. /* wait for right mouse click or ESC or Q or CTRL/C */
  1097. {
  1098.   struct IntuiMessage *msg;
  1099.   ULONG class, sig;
  1100.   UWORD code;
  1101.   BOOL going;
  1102.  
  1103.   going = TRUE;
  1104.   while (going) {
  1105.     sig = Wait ((1 << w->UserPort->mp_SigBit) | SIGBREAKF_CTRL_C);
  1106.     if ((sig & (1 << w->UserPort->mp_SigBit)) != 0) {
  1107.       while ((msg = (struct IntuiMessage *)GetMsg (w->UserPort)) != NULL) {
  1108.         class = msg->Class;
  1109.         code = msg->Code;
  1110.         ReplyMsg ((struct Message *)msg);
  1111.         switch (class) {
  1112.           case IDCMP_VANILLAKEY:
  1113.             switch (code) {
  1114.               case 0x03: /* CTRL/C */
  1115.               case 0x1b: /* ESC */
  1116.               case 'q':  /* q */
  1117.               case 'Q':  /* Q */
  1118.                 going = FALSE;
  1119.                 break;
  1120.               default:
  1121.                 break;
  1122.             }
  1123.             break;
  1124.           case IDCMP_MOUSEBUTTONS:
  1125.             if (code == MENUDOWN)
  1126.               going = FALSE;
  1127.             break;
  1128.           default:
  1129.             break;
  1130.         }
  1131.       }
  1132.     }
  1133.     if ((sig & SIGBREAKF_CTRL_C) != 0)
  1134.       going = FALSE;
  1135.   }
  1136. }
  1137.  
  1138. /****************************************************************************/
  1139.  
  1140. static void display_body (struct mystream *mystream, struct options *opt,
  1141.                           struct BitMapHeader *bmhd, UBYTE *cmap,
  1142.                           int width, int height, int oscan_height, int depth,
  1143.                           char *fname, int which, ULONG intended_mode,
  1144.                           struct EClockVal *eclocks,
  1145.                           struct EClockVal *warp_eclocks,
  1146.                           struct EClockVal *next_time)
  1147. {
  1148.   ULONG size, *colourtable;
  1149.   UBYTE *body, *src, *dst[8];
  1150.   int plane, y, srcbytesperrow;
  1151.   struct BitMap *saved_bm;
  1152.  
  1153.   /* read body */
  1154.   size = CurrentChunk (iff)->cn_Size;
  1155.   if (mystream->f != NULL) {
  1156.     body = (UBYTE *)malloc_check (size);
  1157.     if (ReadChunkBytes (iff, body, size) != size)
  1158.       abort_anim ("Error reading %s", fname);
  1159.   } else
  1160.     body = mystream->rambufptr;
  1161.  
  1162.   /* wait until beam has finished displaying bm[which] */
  1163.   if (!using_intermediate_buffer)
  1164.     wait_until_safe ();
  1165.  
  1166.   /* render body to bm[which] */
  1167.   src = body;
  1168.   srcbytesperrow = RASSIZE (width, 1);
  1169.   for (plane = 0; plane < depth; plane++)
  1170.     dst[plane] = bm[which]->Planes[plane];
  1171.  
  1172.   for (y = 0; y < height; y++) {
  1173.     for (plane = 0; plane < depth; plane++) {
  1174.       switch (bmhd->bmh_Compression) {
  1175.         case cmpNone:
  1176.           memcpy (dst[plane], src, srcbytesperrow);
  1177.           src += srcbytesperrow;
  1178.           break;
  1179.         case cmpByteRun1:
  1180.           if (unpackrow ((BYTE **)&src, dst[plane], srcbytesperrow))
  1181.             abort_anim ("Error unpacking BODY");
  1182.           break;
  1183.         default:
  1184.           abort_anim ("Unrecognised compression");
  1185.       }
  1186.       dst[plane] += bm[which]->BytesPerRow;
  1187.     }
  1188.   }
  1189.  
  1190.   if (mystream->f != NULL)
  1191.     free (body);
  1192.  
  1193.   /* wait until beam has passed displayable bitmap associated with bm[which] */
  1194.   wait_until_safe ();
  1195.  
  1196.   /* copy bm[which] to its associated displayable bitmap */
  1197.   if (using_intermediate_buffer) {
  1198.     if (using_changescreenbuffer) {
  1199.       saved_bm = w->RPort->BitMap;
  1200.       w->RPort->BitMap = sb[which]->sb_BitMap;
  1201.       BltBitMapRastPort (bm[which], 0, 0, w->RPort, 0, 0, width, height, 0xc0);
  1202.       w->RPort->BitMap = saved_bm;
  1203.     } else
  1204.       BltBitMapRastPort (bm[which], 0, 0, w->RPort, 0,
  1205.                          which == 0 ? 0 : oscan_height, width, height, 0xc0);
  1206.   }
  1207.  
  1208.   /* calculate colourtable */
  1209.   colourtable = calc_cmap (s, cmap, 1 << depth, intended_mode);
  1210.  
  1211.   /* wait for time between frames */
  1212.   delay_until (next_time);
  1213.   if (opt->warp)
  1214.     add64 (next_time, warp_eclocks);
  1215.   else
  1216.     add64 (next_time, eclocks);
  1217.  
  1218.   /* wait until previous frame has been displayed */
  1219.   if (using_changescreenbuffer)
  1220.     wait_until_disp ();
  1221.  
  1222.   /* load colourtable */
  1223.   LoadRGB32 (&s->ViewPort, colourtable);
  1224.  
  1225.   /* make bm[which] (or its associated displayable bitmap) visible */
  1226.   if (using_changescreenbuffer) {
  1227.     if (ChangeScreenBuffer (s, sb[which]) != 0) {
  1228.       disp = FALSE;
  1229.       safe = FALSE;
  1230.     }
  1231.   } else {
  1232.     /* MoveScreen (s, which != 0 ? oscan_height : -oscan_height, 0); */
  1233.     s->ViewPort.RasInfo->RyOffset = which != 0 ? oscan_height : 0;
  1234.     ScrollVPort (&s->ViewPort);
  1235.   }
  1236.   waited_until_safe = FALSE;
  1237.  
  1238.   free (colourtable);
  1239.  
  1240.   /* wait until beam has finished displaying bm[1 - which] */
  1241.   if (!using_intermediate_buffer)
  1242.     wait_until_safe ();
  1243.  
  1244.   /* copy bm[which] to bm[1 - which] */
  1245.   for (plane = 0; plane < depth; plane++)
  1246.     memcpy (bm[1 - which]->Planes[plane], bm[which]->Planes[plane],
  1247.             bm[which]->BytesPerRow * bm[which]->Rows);
  1248.  
  1249.   /* wait until beam past displayable bitmap associated with bm[1 - which] */
  1250.   wait_until_safe ();
  1251.  
  1252.   /* copy bm[1 - which] to its displayable bitmap (if bm[] not displayable) */
  1253.   if (using_intermediate_buffer) {
  1254.     if (using_changescreenbuffer) {
  1255.       saved_bm = w->RPort->BitMap;
  1256.       w->RPort->BitMap = sb[1 - which]->sb_BitMap;
  1257.       BltBitMapRastPort (bm[1 - which], 0, 0, w->RPort, 0, 0, width, height, 0xc0);
  1258.       w->RPort->BitMap = saved_bm;
  1259.     } else
  1260.       BltBitMapRastPort (bm[1 - which], 0, 0, w->RPort, 0,
  1261.                          1 - which == 0 ? 0 : oscan_height, width, height,
  1262.                          0xc0);
  1263.   }
  1264. }
  1265.  
  1266. /****************************************************************************/
  1267.  
  1268. static void animate_file (char *fname, struct options opt)
  1269. {
  1270.   int plane, i, width, height, oscan_height, depth, which, ifferror, oldpri;
  1271.   ULONG class, size, mode, intended_mode, totalframes, anh_bits, *colourtable;
  1272.   UWORD code, count, propertymask;
  1273.   WORD *wdata;
  1274.   UBYTE *dlta, anh_operation, anh_interleave;
  1275.   LONG fsize;
  1276.   void *handle;
  1277.   struct IntuiMessage *msg;
  1278.   BOOL going, first_time;
  1279.   struct StoredProperty *bmhdprop, *cmapprop, *camgprop, *anhdprop;
  1280.   struct BitMapHeader *bmhd;
  1281.   struct AnimHeader *anhd;
  1282.   struct DimensionInfo dimsinfo;
  1283.   struct mystream *mystream;
  1284.   char reqtitle[30], *type_string;
  1285.   struct EClockVal eclocks, warp_eclocks, total_eclocks, next_time;
  1286.   struct BitMap *saved_bm;
  1287.   static struct Hook smr_filter_hook = {
  1288.     {NULL},
  1289.     (ULONG (*)())smr_filter,
  1290.     NULL,
  1291.     NULL
  1292.   };
  1293.   static struct Rectangle rect;
  1294.  
  1295.  
  1296.   anh_operation = 0;
  1297.   anh_interleave = 0;
  1298.   anh_bits = 0;
  1299.  
  1300.   /* checkpoint for abort_anim() */
  1301.   if (setjmp (err_jmp) != 0)
  1302.     return;
  1303.  
  1304.   parse_tooltypes (fname, &opt);
  1305.  
  1306.   if ((iff = AllocIFF ()) == NULL)
  1307.     abort_anim ("AllocIFF() failed");
  1308.  
  1309.   mystream = (struct mystream *)malloc_check (sizeof(struct mystream));
  1310.   memset (mystream, 0, sizeof(struct mystream));
  1311.  
  1312.   if ((mystream->f = fopen (fname, "r")) == 0)
  1313.     abort_anim ("Can't open %s", fname);
  1314.   if (fseek (mystream->f, 0, SEEK_END) == -1 ||
  1315.       (fsize = ftell (mystream->f)) == -1)
  1316.     abort_anim ("Error seeking %s", fname);
  1317.   rewind (mystream->f);
  1318.  
  1319.   if (opt.ram && (mystream->rambuf = malloc (fsize)) != NULL) {
  1320.     if (fread (mystream->rambuf, 1, fsize, mystream->f) != fsize ||
  1321.         fclose (mystream->f) == EOF)
  1322.       abort_anim ("Error reading %s", fname);
  1323.     mystream->f = NULL;
  1324.     mystream->rambufptr = mystream->rambuf;
  1325.     mystream->rambufsize = fsize;
  1326.   } else {
  1327.     if (opt.ram)
  1328.       printf ("Not enough memory to play from RAM, playing from DISK instead\n");
  1329.     mystream->rambuf = NULL;
  1330.   }
  1331.  
  1332.   iff->iff_Stream = (ULONG)mystream;
  1333.   InitIFF (iff, IFFF_FSEEK | IFFF_RSEEK, &mystreamhook);
  1334.  
  1335.   if (OpenIFF (iff, IFFF_READ) != 0)
  1336.     abort_anim ("OpenIFF() failed");
  1337.   iff_is_open = TRUE;
  1338.  
  1339.   if (PropChunk (iff, ID_ILBM, ID_BMHD) != 0 ||
  1340.       PropChunk (iff, ID_ILBM, ID_ANHD) != 0 ||
  1341.       PropChunk (iff, ID_ILBM, ID_CMAP) != 0 ||
  1342.       PropChunk (iff, ID_ILBM, ID_CAMG) != 0 ||
  1343.       StopChunk (iff, ID_ILBM, ID_BODY) != 0)
  1344.     abort_anim ("Error calling PropChunk or StopChunk parsing %s", fname);
  1345.   if ((ifferror = ParseIFF (iff, IFFPARSE_SCAN)) != 0)
  1346.     abort_anim ("%s: IFF error %d parsing %s", programname, ifferror, fname);
  1347.   if ((bmhdprop = FindProp (iff, ID_ILBM, ID_BMHD)) == NULL ||
  1348.       (cmapprop = FindProp (iff, ID_ILBM, ID_CMAP)) == NULL)
  1349.     abort_anim ("%s: Missing BMHD or CMAP parsing %s", programname, fname);
  1350.  
  1351.   bmhd = (struct BitMapHeader *)bmhdprop->sp_Data;
  1352.  
  1353.   width = bmhd->bmh_Width;
  1354.   height = bmhd->bmh_Height;
  1355.   depth = bmhd->bmh_Depth;
  1356.   if ((camgprop = FindProp (iff, ID_ILBM, ID_CAMG)) != NULL)
  1357.     intended_mode = *(ULONG *)camgprop->sp_Data;
  1358.   else
  1359.     intended_mode = 0;
  1360.  
  1361.   /* display file attributes */
  1362.   printf ("\nFile = %s, size = %ld bytes\n", fname, fsize);
  1363.   printf ("Anim size %ux%ux%u\n", width, height, depth);
  1364.   printf ("Intended ModeID = $%08lx \"%s\"\n", intended_mode,
  1365.           mode_name (intended_mode));
  1366.  
  1367.   type_string = "";
  1368.   propertymask = DIPF_IS_EXTRAHALFBRITE | DIPF_IS_DUALPF | DIPF_IS_PF2PRI |
  1369.                  DIPF_IS_HAM;
  1370.   if (camgprop != NULL &&
  1371.       (*(ULONG *)camgprop->sp_Data & EXTRAHALFBRITE_KEY) != 0) {
  1372.     /* printf ("  (EXTRAHALFBRITE)"); */
  1373.     type_string = " EHB";
  1374.     propertymask &= ~DIPF_IS_EXTRAHALFBRITE;
  1375.   }
  1376.   if (camgprop != NULL &&
  1377.       (*(ULONG *)camgprop->sp_Data & HAM_KEY) != 0) {
  1378.     switch (depth) {
  1379.       case 6:
  1380.         /* printf ("  (HAM)"); */
  1381.         type_string = " HAM6";
  1382.         break;
  1383.       case 8:
  1384.         /* printf ("  (HAM8)"); */
  1385.         type_string = " HAM8";
  1386.         break;
  1387.       default:
  1388.         abort_anim ("HAM but not depth 6 or 8");
  1389.     }
  1390.     propertymask &= ~DIPF_IS_HAM;
  1391.   }
  1392.   /* printf ("\n"); */
  1393.  
  1394.   first_time = TRUE;
  1395.   warp_eclocks.ev_hi = 0;
  1396.   warp_eclocks.ev_lo = 0;
  1397.   eclocks.ev_hi = 0;
  1398.   eclocks.ev_lo = 0;
  1399.   total_eclocks.ev_hi = 0;
  1400.   total_eclocks.ev_lo = 0;
  1401.   if ((anhdprop = FindProp (iff, ID_ILBM, ID_ANHD)) != NULL) {
  1402.     anhd = (struct AnimHeader *)anhdprop->sp_Data;
  1403.     anh_operation = anhd->anh_Operation;
  1404.     anh_interleave = anhd->anh_Interleave;
  1405.     anh_bits = anhd->anh_Bits;
  1406.     eclocks.ev_lo = (ULONG)((1000000.0 / 60.0) * anhd->anh_Reltime /
  1407.                     micros_per_eclock + 0.5);
  1408.     add64 (&total_eclocks, &eclocks);
  1409.   }
  1410.  
  1411.   if (opt.mode != INVALID_ID)
  1412.  
  1413.     mode = opt.mode;
  1414.  
  1415.   else {
  1416.  
  1417.     /* try to guess the best screen mode */
  1418.     if (CyberGfxBase != NULL)
  1419.       mode = BestCModeIDTags (CYBRBIDTG_NominalWidth, max(width, 320),
  1420.                               CYBRBIDTG_NominalHeight, max(height, 200),
  1421.                               CYBRBIDTG_Depth, max(depth, 4),
  1422.                               TAG_DONE);
  1423.     else if (GfxBase->LibNode.lib_Version >= 39)
  1424.       mode = BestModeID (BIDTAG_NominalWidth, width,
  1425.                          BIDTAG_NominalHeight, height,
  1426.                          BIDTAG_Depth, depth,
  1427.                          BIDTAG_DIPFMustNotHave, propertymask,
  1428.                          TAG_DONE);
  1429.     else
  1430.       mode = 0;
  1431.  
  1432.     /* put up mode requester */
  1433.     if (opt.modereq) {
  1434.       sprintf (reqtitle, "%s %dx%d%s", programname, width, height, type_string);
  1435.       if (!AslRequestTags (smr,
  1436.                            ASLSM_TitleText,     (ULONG)reqtitle,
  1437.                            ASLSM_InitialDisplayID, mode,
  1438.                            ASLSM_MinWidth,      width,
  1439.                            ASLSM_MinHeight,     height,
  1440.                            ASLSM_MinDepth,      depth,
  1441.                            ASLSM_MaxDepth,      8,
  1442.                            ASLSM_PropertyMask,  propertymask,
  1443.                            ASLSM_PropertyFlags, 0,
  1444.                            ASLSM_FilterFunc,    &smr_filter_hook,
  1445.                            TAG_DONE))
  1446.         abort_anim ("ScreenMode requester failed or cancelled");
  1447.       mode = smr->sm_DisplayID;
  1448.     }
  1449.   }
  1450.  
  1451.   printf ("Using ModeID =    $%08lx \"%s\"\n", mode, mode_name (mode));
  1452.  
  1453.   if ((handle = FindDisplayInfo (mode)) != NULL &&
  1454.       (count = GetDisplayInfoData (handle, (UBYTE *)&dimsinfo,
  1455.                                    sizeof(struct DimensionInfo), DTAG_DIMS,
  1456.                                    NULL)) < 66
  1457.                                              /* sizeof(struct DimensionInfo) */)
  1458.     abort_anim ("GetDisplayInfoData(Dims) failed (%d)", count);
  1459.  
  1460.   oscan_height = max(height, dimsinfo.MaxOScan.MaxY - dimsinfo.MaxOScan.MinY + 1);
  1461.  
  1462.   if (GfxBase->LibNode.lib_Version < 39 ||
  1463.       (CyberGfxBase != NULL && IsCyberModeID (mode)))
  1464.     opt.changescreenbuffer = FALSE;
  1465.   using_changescreenbuffer = opt.changescreenbuffer;
  1466.   using_waittof = opt.waittof;
  1467.   using_waitbovp = opt.waitbovp;
  1468.  
  1469.   rect.MinX = 0;
  1470.   rect.MaxX = width - 1;
  1471.   rect.MinY = 0;
  1472.   rect.MaxY = height - 1;
  1473.  
  1474.   if ((s = OpenScreenTags (NULL,
  1475.                            SA_DisplayID, mode,
  1476.                            SA_DClip, (ULONG)&rect,
  1477.                            SA_Width, width,
  1478.                            SA_Height, using_changescreenbuffer
  1479.                                       ? oscan_height
  1480.                                       : oscan_height << 1,
  1481.                            SA_Depth, max (depth, 4),
  1482.                            /* SA_Draggable,FALSE, */
  1483.                            /* SA_AutoScroll,FALSE, */
  1484.                            /* SA_Exclusive,TRUE, */
  1485.                            SA_Quiet,TRUE,
  1486.                            SA_Type, CUSTOMSCREEN,
  1487.                            TAG_DONE)) == NULL)
  1488.     abort_anim ("Can't open Screen (mode $%08x, depth %d)", mode, max(depth, 4));
  1489.  
  1490.   colourtable = calc_cmap (s, (UBYTE *)cmapprop->sp_Data, 1 << depth,
  1491.                            intended_mode);
  1492.   LoadRGB32 (&s->ViewPort, colourtable);
  1493.   free (colourtable);
  1494.  
  1495.   printf ("Screen size %ux%u\n", s->Width, s->Height);
  1496.  
  1497.   if ((w = OpenWindowTags (NULL,
  1498.                            WA_Left,   0,
  1499.                            WA_Top,    0,
  1500.                            WA_Width,  width,
  1501.                            WA_Height, s->ViewPort.RasInfo->BitMap->Rows,
  1502.                            WA_IDCMP,  IDCMP_VANILLAKEY | IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS,
  1503.                            WA_Flags,  WFLG_ACTIVATE | WFLG_BORDERLESS | WFLG_RMBTRAP,
  1504.                            WA_CustomScreen, s,
  1505.                            TAG_DONE)) == NULL)
  1506.     abort_anim ("Can't open Window");
  1507.  
  1508.   emptypointer = (WORD *)AllocMem (12, MEMF_CHIP | MEMF_CLEAR);
  1509.   SetPointer (w, emptypointer, 1, 16, 0, 0);
  1510.  
  1511.   /* allocate ports for changescreenbuffer */
  1512.   if (using_changescreenbuffer) {
  1513.     if ((safeport = CreatePort (NULL, 0)) == NULL ||
  1514.         (dispport = CreatePort (NULL, 0)) == NULL)
  1515.       abort_anim ("Can't create port!");
  1516.     Signal (FindTask (NULL),
  1517.             (1 << safeport->mp_SigBit) | (1 << dispport->mp_SigBit));
  1518.   }
  1519.   waited_until_safe = FALSE;
  1520.  
  1521.   /* allocate bitmaps and screenbuffers */
  1522.   for (i = 0; i < NBITMAPS; i++) {
  1523.     if (CyberGfxBase != NULL && IsCyberModeID (GetVPModeID (&s->ViewPort))) {
  1524.       if (using_changescreenbuffer)
  1525.         abort_anim ("Internal error 1");
  1526.       using_fastmem_bitmap = TRUE;
  1527.       using_intermediate_buffer = TRUE;
  1528.       /* allocate separate bitmaps in fastmem for cybergraphics */
  1529.       bm[i] = malloc_check (sizeof(struct BitMap));
  1530.       memset (bm[i], 0, sizeof(struct BitMap));
  1531.       InitBitMap (bm[i], depth, width, height);
  1532.       for (plane = 0; plane < bm[i]->Depth; plane++)
  1533.         bm[i]->Planes[plane] = malloc_check (bm[i]->BytesPerRow * bm[i]->Rows);
  1534.     } else if (GfxBase->LibNode.lib_Version >= 39 &&
  1535.                (GetBitMapAttr (s->ViewPort.RasInfo->BitMap, BMA_FLAGS) &
  1536.                                BMF_STANDARD) != 0 &&
  1537.                (GetBitMapAttr (s->ViewPort.RasInfo->BitMap, BMA_FLAGS) &
  1538.                                BMF_INTERLEAVED) == 0) {
  1539.       using_fastmem_bitmap = FALSE;
  1540.       using_intermediate_buffer = FALSE;
  1541.       if (using_changescreenbuffer) {
  1542.         /* use screen's bitmap and allocate another bitmap for dbuf */
  1543.         if ((sb[i] = AllocScreenBuffer (s, NULL, i == 0 ? SB_SCREEN_BITMAP : 0)) == NULL)
  1544.           abort_anim ("Can't allocate structure for double buffering");
  1545.         sb[i]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort = safeport;
  1546.         sb[i]->sb_DBufInfo->dbi_DispMessage.mn_ReplyPort = dispport;
  1547.         bm[i] = sb[i]->sb_BitMap;
  1548.       } else {
  1549.         /* bitmaps point to top and bottom halves of screen's bitmap */
  1550.         bm[i] = malloc_check (sizeof(struct BitMap));
  1551.         memset (bm[i], 0, sizeof(struct BitMap));
  1552.         bm[i]->BytesPerRow = s->ViewPort.RasInfo->BitMap->BytesPerRow;
  1553.         bm[i]->Rows = oscan_height;
  1554.         bm[i]->Flags = s->ViewPort.RasInfo->BitMap->Flags;
  1555.         bm[i]->Depth = s->ViewPort.RasInfo->BitMap->Depth;
  1556.         for (plane = 0; plane < bm[i]->Depth; plane++)
  1557.           bm[i]->Planes[plane] = &s->ViewPort.RasInfo->BitMap->Planes[plane]
  1558.                                   [i * bm[i]->BytesPerRow * oscan_height];
  1559.       }
  1560.     } else {
  1561.       using_fastmem_bitmap = FALSE;
  1562.       using_intermediate_buffer = TRUE;
  1563.       /* generic code for any gfx system --- use separate bitmaps */
  1564.       if ((bm[i] = AllocBitMap (width, height, depth, 0, NULL)) == NULL)
  1565.         abort_anim ("Can't allocate bitmap");
  1566.       if (using_changescreenbuffer) {
  1567.         if ((sb[i] = AllocScreenBuffer (s, NULL, i == 0 ? SB_SCREEN_BITMAP : 0)) == NULL)
  1568.           abort_anim ("Can't allocate structure for double buffering");
  1569.         sb[i]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort = safeport;
  1570.         sb[i]->sb_DBufInfo->dbi_DispMessage.mn_ReplyPort = dispport;
  1571.       }
  1572.     }
  1573.   }
  1574.   printf ("Bitmap size %ux%ux%u\n", bm[0]->BytesPerRow << 3,
  1575.           bm[0]->Rows, bm[0]->Depth);
  1576.   if (!using_fastmem_bitmap)
  1577.     printf ("Using CHIPMEM\n");
  1578.   if (!using_intermediate_buffer)
  1579.     printf ("Using direct rendering\n");
  1580.   if (using_changescreenbuffer)
  1581.     printf ("Using ChangeScreenBuffer()\n");
  1582.  
  1583.   dirty = (WORD *)malloc_check ((((bm[0]->BytesPerRow + 3) >> 2) << 1)
  1584.                                   * sizeof(WORD));
  1585.  
  1586.   /* PrintTopChunk (iff); */
  1587.  
  1588.   /* read the start time */
  1589.   ReadEClock (time0);
  1590.   next_time = *time0;
  1591.   add64 (&next_time, &eclocks);
  1592.  
  1593.   if (!using_intermediate_buffer ||
  1594.       (anh_interleave != 1 || (anh_bits & anfXor) == 0))
  1595.     which = 1;
  1596.   else
  1597.     which = 0;
  1598.  
  1599.   display_body (mystream, &opt, bmhd, (UBYTE *)cmapprop->sp_Data, width, height,
  1600.                 oscan_height, depth, fname, which, intended_mode,
  1601.                 &eclocks, &warp_eclocks, &next_time);
  1602.  
  1603.   totalframes = 1;
  1604.   going = TRUE;
  1605.  
  1606.   /* loop for each frame */
  1607.   while (going) {
  1608.  
  1609.     /* check for input */
  1610.     while ((msg = (struct IntuiMessage *)GetMsg (w->UserPort)) != NULL) {
  1611.       class = msg->Class;
  1612.       code = msg->Code;
  1613.       ReplyMsg ((struct Message *)msg);
  1614.       switch (class) {
  1615.         case IDCMP_VANILLAKEY:
  1616.           switch (code) {
  1617.             case 0x03: /* CTRL/C */
  1618.             case 0x1b: /* ESC */
  1619.             case 'q':  /* q */
  1620.             case 'Q':  /* Q */
  1621.               going = FALSE;
  1622.               break;
  1623.             case 'l':  /* l */
  1624.             case 'L':  /* L */
  1625.               opt.loopframes = !opt.loopframes;
  1626.               break;
  1627.             default:
  1628.               break;
  1629.           }
  1630.           break;
  1631.         case IDCMP_RAWKEY:
  1632.           switch (code) {
  1633.             case 0x50: /* F1 */
  1634.               warp_eclocks.ev_lo = 0;
  1635.               opt.warp = TRUE;
  1636.               break;
  1637.             case 0x51: /* F2 */
  1638.               warp_eclocks.ev_lo = (ULONG)((1000000.0 / 60.0) / micros_per_eclock + 0.5);
  1639.               opt.warp = TRUE;
  1640.               break;
  1641.             case 0x52: /* F3 */
  1642.               warp_eclocks.ev_lo = (ULONG)((1000000.0 / 30.0) / micros_per_eclock + 0.5);
  1643.               opt.warp = TRUE;
  1644.               break;
  1645.             case 0x53: /* F4 */
  1646.               warp_eclocks.ev_lo = (ULONG)((1000000.0 / 24.0) / micros_per_eclock + 0.5);
  1647.               opt.warp = TRUE;
  1648.               break;
  1649.             case 0x54: /* F5 */
  1650.               warp_eclocks.ev_lo = (ULONG)((1000000.0 / 15.0) / micros_per_eclock + 0.5);
  1651.               opt.warp = TRUE;
  1652.               break;
  1653.             case 0x55: /* F6 */
  1654.               warp_eclocks.ev_lo = (ULONG)((1000000.0 / 12.0) / micros_per_eclock + 0.5);
  1655.               opt.warp = TRUE;
  1656.               break;
  1657.             case 0x56: /* F7 */
  1658.               warp_eclocks.ev_lo = (ULONG)((1000000.0 / 10.0) / micros_per_eclock + 0.5);
  1659.               opt.warp = TRUE;
  1660.               break;
  1661.             case 0x57: /* F8 */
  1662.               warp_eclocks.ev_lo = (ULONG)((1000000.0 / 5.0) / micros_per_eclock + 0.5);
  1663.               opt.warp = TRUE;
  1664.               break;
  1665.             case 0x58: /* F9 */
  1666.               warp_eclocks.ev_lo = (ULONG)(1000000.0 / micros_per_eclock + 0.5);
  1667.               opt.warp = TRUE;
  1668.               break;
  1669.             case 0x59: /* F10 */
  1670.               opt.warp = FALSE;
  1671.               break;
  1672.             default:
  1673.               break;
  1674.           }
  1675.           break;
  1676.         case IDCMP_MOUSEBUTTONS:
  1677.           if (code == MENUDOWN)
  1678.             going = FALSE;
  1679.           break;
  1680.         default:
  1681.           break;
  1682.       }
  1683.     }
  1684.  
  1685. #ifdef __SASC
  1686.     chkabort ();
  1687. #endif
  1688.  
  1689.     /* PrintTopChunk (iff); */
  1690.  
  1691.     while ((ifferror = ParseIFF (iff, IFFPARSE_RAWSTEP)) == IFFERR_EOC)
  1692.       /* do nothing */ ;
  1693.  
  1694.     /* check for end of file */
  1695.     if (ifferror == IFFERR_EOF) {
  1696.       if (opt.once)
  1697.         break;  /* break out of while (going) */
  1698.       else {
  1699.         /* found end of file --- rewind and seek to 2nd DLTA */
  1700.         CloseIFF (iff);
  1701.         iff_is_open = FALSE;
  1702.         if (mystream->f != NULL)
  1703.           rewind (mystream->f);
  1704.         else if (mystream->rambuf != NULL)
  1705.           mystream->rambufptr = mystream->rambuf;
  1706.         else
  1707.           abort_anim ("Internal error");
  1708.         if (OpenIFF (iff, IFFF_READ) != 0)
  1709.           abort_anim ("OpenIFF() failed");
  1710.         iff_is_open = TRUE;
  1711.  
  1712.         if (opt.loopframes) {
  1713.  
  1714.           /* seek past body */
  1715.  
  1716.           if (StopChunk (iff, ID_ILBM, ID_BODY) != 0)
  1717.             abort_anim ("Error calling StopChunk parsing %s", fname);
  1718.           if ((ifferror = ParseIFF (iff, IFFPARSE_SCAN)) != 0)
  1719.             abort_anim ("IFF error %d parsing %s for BODY", ifferror, fname);
  1720.  
  1721.           if (anh_operation != cmpBytedelta || anh_interleave != 1) {/* if not animbrush */
  1722.  
  1723.             /* seek past first DLTA */
  1724.             while ((ifferror = ParseIFF (iff, IFFPARSE_RAWSTEP)) == IFFERR_EOC)
  1725.               /* do nothing */ ;
  1726.             if (StopChunk (iff, ID_ILBM, ID_DLTA) != 0)
  1727.               abort_anim ("Error calling StopChunk parsing %s", fname);
  1728.             if ((ifferror = ParseIFF (iff, IFFPARSE_SCAN)) != 0) {
  1729.               if (ifferror != IFFERR_EOF)
  1730.                 abort_anim ("IFF error %d parsing %s for DLTA", ifferror, fname);
  1731.               else {
  1732.                 /* static picture --- display BODY and wait for quit event */
  1733.                 wait_for_click ();
  1734.                 going = FALSE;
  1735.               }
  1736.             }
  1737.           }
  1738.  
  1739.         } else { /* no loop frames --- redisplay body */
  1740.  
  1741.           if (PropChunk (iff, ID_ILBM, ID_ANHD) != 0 ||
  1742.               PropChunk (iff, ID_ILBM, ID_BMHD) != 0 ||
  1743.               PropChunk (iff, ID_ILBM, ID_CMAP) != 0 ||
  1744.               StopChunk (iff, ID_ILBM, ID_BODY) != 0)
  1745.             abort_anim ("Error calling PropChunk or StopChunk parsing %s", fname);
  1746.           if ((ifferror = ParseIFF (iff, IFFPARSE_SCAN)) != 0)
  1747.             abort_anim ("%s: IFF error %d parsing %s", programname, ifferror, fname);
  1748.  
  1749.           if ((anhdprop = FindProp (iff, ID_ILBM, ID_ANHD)) != NULL) {
  1750.             anhd = (struct AnimHeader *)anhdprop->sp_Data;
  1751.             anh_operation = anhd->anh_Operation;
  1752.             anh_interleave = anhd->anh_Interleave;
  1753.             anh_bits = anhd->anh_Bits;
  1754.             eclocks.ev_lo = (ULONG)((1000000.0 / 60.0) * anhd->anh_Reltime /
  1755.                             micros_per_eclock + 0.5);
  1756.             add64 (&total_eclocks, &eclocks);
  1757.           }
  1758.  
  1759.           if ((bmhdprop = FindProp (iff, ID_ILBM, ID_BMHD)) == NULL ||
  1760.               (cmapprop = FindProp (iff, ID_ILBM, ID_CMAP)) == NULL)
  1761.             abort_anim ("%s: Missing BMHD or CMAP parsing %s", programname, fname);
  1762.  
  1763.           bmhd = (struct BitMapHeader *)bmhdprop->sp_Data;
  1764.  
  1765.           /* render to the other bitmap */
  1766.           if (!using_intermediate_buffer ||
  1767.               (anh_interleave != 1 || (anh_bits & anfXor) == 0))
  1768.             which = 1 - which;
  1769.  
  1770.           display_body (mystream, &opt,
  1771.                         (struct BitMapHeader *)bmhdprop->sp_Data,
  1772.                         (UBYTE *)cmapprop->sp_Data, width, height,
  1773.                         oscan_height, depth, fname, which, intended_mode,
  1774.                         &eclocks, &warp_eclocks, &next_time);
  1775.  
  1776.           totalframes++;
  1777.  
  1778.         }
  1779.  
  1780.         continue;   /* file rewound, restart from while (going)  */
  1781.  
  1782.       }
  1783.     } else if (ifferror != 0) {
  1784.       abort_anim ("IFF error %d parsing %s", ifferror, fname);
  1785.     }
  1786.  
  1787.     /* PrintTopChunk (iff); */
  1788.  
  1789.     if (PropChunk (iff, ID_ILBM, ID_ANHD) != 0 ||
  1790.         PropChunk (iff, ID_ILBM, ID_CMAP) != 0 ||
  1791.         StopChunk (iff, ID_ILBM, ID_DLTA) != 0)
  1792.       abort_anim ("Error calling PropChunk or StopChunk parsing %s", fname);
  1793.     if ((ifferror = ParseIFF (iff, IFFPARSE_SCAN)) != 0)
  1794.       abort_anim ("IFF error %d parsing %s", ifferror, fname);
  1795.     if ((anhdprop = FindProp (iff, ID_ILBM, ID_ANHD)) == NULL)
  1796.       abort_anim ("missing ANHD chunk parsing %s", fname);
  1797.  
  1798.     anhd = (struct AnimHeader *)anhdprop->sp_Data;
  1799.     anh_operation = anhd->anh_Operation;
  1800.     anh_interleave = anhd->anh_Interleave;
  1801.     anh_bits = anhd->anh_Bits;
  1802.     eclocks.ev_lo = (ULONG)((1000000.0 / 60.0) * anhd->anh_Reltime /
  1803.                     micros_per_eclock + 0.5);
  1804.     add64 (&total_eclocks, &eclocks);
  1805.     if (first_time) {
  1806.       printf ("Anim type %d, abstime %ld, reltime %ld, anh_interleave %d, flags %08x\n",
  1807.               (int)anh_operation, anhd->anh_Abstime, anhd->anh_Reltime,
  1808.               (int)anh_interleave, anh_bits);
  1809.       first_time = FALSE;
  1810.     }
  1811.  
  1812.     size = CurrentChunk (iff)->cn_Size;
  1813.     /* printf ("DLTA size %d at %d\n", size, CurrentChunk (iff)->cn_Scan); */
  1814.     if (mystream->f != NULL) {
  1815.       dlta = malloc_check (size);
  1816.       if (ReadChunkBytes (iff, dlta, size) != size)
  1817.         abort_anim ("Error reading %s", fname);
  1818.     } else
  1819.       dlta = mystream->rambufptr;
  1820.  
  1821.     /* render to the other bitmap */
  1822.     if (!using_intermediate_buffer ||
  1823.         (anh_interleave != 1 || (anh_bits & anfXor) == 0))
  1824.       which = 1 - which;
  1825.  
  1826.     if (using_intermediate_buffer)
  1827.       memset (dirty, -1, (((bm[0]->BytesPerRow + 3) >> 2) << 1) * sizeof(WORD));
  1828.     else
  1829.       /* wait until beam has finished displaying bm[which] */
  1830.       wait_until_safe ();
  1831.  
  1832.     /* unpack DLTA to bm[which] */
  1833.     for (plane = 0; plane < depth; plane++) {
  1834.       wdata = (WORD *)&dlta[((ULONG *)dlta)[plane]];
  1835.       /* printf ("%d %08x %ld\n", plane, wdata, ((ULONG *)dlta)[plane]); */
  1836.  
  1837.       switch (anh_operation) {
  1838.  
  1839.         case cmpLongdelta:    /* 2 */
  1840.           if (wdata != (WORD *)dlta)
  1841.             unpacklongdelta (wdata, (LONG *)bm[which]->Planes[plane],
  1842.                              bm[which]->BytesPerRow, dirty);
  1843.           break;
  1844.  
  1845.         case cmpShortdelta:    /* 3 */
  1846.           if (wdata != (WORD *)dlta)
  1847.             unpackshortdelta (wdata, (WORD *)bm[which]->Planes[plane],
  1848.                              bm[which]->BytesPerRow, dirty);
  1849.           break;
  1850.  
  1851.         case cmpBytedelta:    /* 5 */
  1852.           if (wdata != (WORD *)dlta)
  1853.             if (using_intermediate_buffer)
  1854.               if (anh_interleave == 1 && (anh_bits & anfXor) != 0)
  1855.                 unpackbytedeltaxor ((BYTE *)wdata, bm[which]->Planes[plane],
  1856.                                     bm[which]->BytesPerRow, dirty);
  1857.               else
  1858.                 unpackbytedelta ((BYTE *)wdata, bm[which]->Planes[plane],
  1859.                                  bm[which]->BytesPerRow, dirty);
  1860.             else
  1861.               if (anh_interleave == 1 && (anh_bits & anfXor) != 0)
  1862.                 unpackbytedeltanodirtyxor ((BYTE *)wdata,
  1863.                                            bm[which]->Planes[plane],
  1864.                                            bm[1-which]->Planes[plane],
  1865.                                            bm[which]->BytesPerRow, height);
  1866.               else
  1867.                 unpackbytedeltanodirty ((BYTE *)wdata, bm[which]->Planes[plane],
  1868.                                         bm[which]->BytesPerRow);
  1869.           break;
  1870.  
  1871.         case cmpAnim7:        /* 7 */
  1872.           if (wdata != (WORD *)dlta)
  1873.             if ((anh_bits & anfLongdata) != 0)
  1874.               if (using_intermediate_buffer)
  1875.                 unpackanim7long ((BYTE *)wdata,
  1876.                                  (LONG *)&dlta[((ULONG *)dlta)[plane + 8]],
  1877.                                  bm[which]->Planes[plane],
  1878.                                  bm[which]->BytesPerRow,
  1879.                                  dirty);
  1880.               else
  1881.                 unpackanim7longnodirty ((BYTE *)wdata,
  1882.                                         (LONG *)&dlta[((ULONG *)dlta)[plane + 8]],
  1883.                                         bm[which]->Planes[plane],
  1884.                                         bm[which]->BytesPerRow);
  1885.             else
  1886.               if (using_intermediate_buffer)
  1887.                 unpackanim7word ((BYTE *)wdata,
  1888.                                  (WORD *)&dlta[((ULONG *)dlta)[plane + 8]],
  1889.                                  bm[which]->Planes[plane],
  1890.                                  bm[which]->BytesPerRow,
  1891.                                  dirty);
  1892.               else
  1893.                 unpackanim7wordnodirty ((BYTE *)wdata,
  1894.                                         (WORD *)&dlta[((ULONG *)dlta)[plane + 8]],
  1895.                                         bm[which]->Planes[plane],
  1896.                                         bm[which]->BytesPerRow);
  1897.           break;
  1898.  
  1899.         case cmpAnim8:        /* 8 */
  1900.           if (wdata != (WORD *)dlta)
  1901.             if ((anh_bits & anfLongdata) != 0)
  1902.               if (using_intermediate_buffer)
  1903.                 unpackanim8long ((LONG *)wdata,
  1904.                                  bm[which]->Planes[plane],
  1905.                                  bm[which]->BytesPerRow,
  1906.                                  dirty);
  1907.               else
  1908.                 unpackanim8longnodirty ((LONG *)wdata,
  1909.                                         bm[which]->Planes[plane],
  1910.                                         bm[which]->BytesPerRow);
  1911.             else
  1912.               if (using_intermediate_buffer)
  1913.                 unpackanim8word ((WORD *)wdata,
  1914.                                  bm[which]->Planes[plane],
  1915.                                  bm[which]->BytesPerRow,
  1916.                                  dirty);
  1917.               else
  1918.                 unpackanim8wordnodirty ((WORD *)wdata,
  1919.                                         bm[which]->Planes[plane],
  1920.                                         bm[which]->BytesPerRow);
  1921.           break;
  1922.  
  1923.         case cmpDirect:        /* 0 */
  1924.         case cmpXor:        /* 1 */
  1925.         case cmpDelta:        /* 4 */
  1926.         case cmpStereo:        /* 6 */
  1927.         case cmpJ:        /* 74 */
  1928.         default:
  1929.           abort_anim ("Anim type %d not recognised", (int)anh_operation);
  1930.       }
  1931.     }
  1932.  
  1933.     if (mystream->f != NULL)
  1934.       free (dlta);
  1935.  
  1936.     if (using_intermediate_buffer) {
  1937.  
  1938.       /* wait until beam has passed displayable bitmap for bm[which] */
  1939.       wait_until_safe ();
  1940.  
  1941.       /* copy bm[which] to its displayable bitmap (if bm[] not displayable) */
  1942.       if (using_changescreenbuffer) {
  1943.         saved_bm = w->RPort->BitMap;
  1944.         w->RPort->BitMap = sb[which]->sb_BitMap;
  1945.       }
  1946.       if (anh_operation == cmpBytedelta ||
  1947.           anh_operation == cmpAnim7 ||
  1948.           anh_operation == cmpAnim8)
  1949.         blit_opt (bm[which], w->RPort, dirty,
  1950.                   which == 0 || using_changescreenbuffer ? 0 : oscan_height);
  1951.       else
  1952.         BltBitMapRastPort (bm[which], 0, 0, w->RPort, 0,
  1953.                            which == 0 || using_changescreenbuffer ? 0 : oscan_height,
  1954.                            width, height, 0xc0);
  1955.       if (using_changescreenbuffer) {
  1956.         w->RPort->BitMap = saved_bm;
  1957.       }
  1958.     }
  1959.  
  1960.     /* calculate colourtable if required */
  1961.     if ((cmapprop = FindProp (iff, ID_ILBM, ID_CMAP)) != NULL)
  1962.       colourtable = calc_cmap (s, (UBYTE *)cmapprop->sp_Data, 1 << depth,
  1963.                                intended_mode);
  1964.  
  1965.     /* wait for time between frames */
  1966.     delay_until (&next_time);
  1967.     if (opt.warp)
  1968.       add64 (&next_time, &warp_eclocks);
  1969.     else
  1970.       add64 (&next_time, &eclocks);
  1971.  
  1972.     /* temporarily increase priority to reduce palette change flicker */
  1973.     oldpri = SetTaskPri (FindTask(NULL), 20);
  1974.  
  1975.     /* wait until previous frame has been displayed */
  1976.     if (using_changescreenbuffer)
  1977.       wait_until_disp ();
  1978.  
  1979.     /* load colourtable */
  1980.     if (cmapprop != NULL)
  1981.       LoadRGB32 (&s->ViewPort, colourtable);
  1982.  
  1983.     /* make bm[which] (or its associated displayable bitmap) visible */
  1984.     if (using_changescreenbuffer) {
  1985.       if (ChangeScreenBuffer (s, sb[which]) != 0) {
  1986.         disp = FALSE;
  1987.         safe = FALSE;
  1988.       }
  1989.     } else {
  1990.       /* MoveScreen (s, which != 0 ? oscan_height : -oscan_height, 0); */
  1991.       s->ViewPort.RasInfo->RyOffset = which != 0 ? oscan_height : 0;
  1992.       ScrollVPort (&s->ViewPort);
  1993.     }
  1994.     waited_until_safe = FALSE;
  1995.  
  1996.     /* restore task priority */
  1997.     SetTaskPri (FindTask(NULL), oldpri);
  1998.  
  1999.     if (cmapprop != NULL)
  2000.       free (colourtable);
  2001.  
  2002.     totalframes++;
  2003.  
  2004.   }  /* end of loop for each frame */
  2005.  
  2006.   /* find out and display how long it took */
  2007.   ReadEClock (time1);
  2008.   if (total_eclocks.ev_hi != 0 || total_eclocks.ev_lo != 0)
  2009.     printf ("%s: Intended frames per second = %4.1lf\n", programname,
  2010.             1000000.0 * totalframes /
  2011.             ((total_eclocks.ev_hi * 4294967296.0 + total_eclocks.ev_lo) *
  2012.              micros_per_eclock));
  2013.   sub64 (time1, time0);
  2014.   printf ("%s: Achieved frames per second = %4.1lf\n", programname,
  2015.           1000000.0 * totalframes /
  2016.           ((time1->ev_hi * 4294967296.0 + time1->ev_lo) * micros_per_eclock));
  2017.  
  2018.   partial_cleanup ();
  2019. }
  2020.  
  2021. /****************************************************************************/
  2022.  
  2023. static void filerequestloop (struct options *opt)
  2024. {
  2025.   static char resultstring[128];
  2026.  
  2027.   if (AslBase != NULL) {
  2028.     while (AslRequest (fr, NULL)) {
  2029.       strcpy (resultstring, fr->rf_Dir);
  2030.       AddPart (resultstring, fr->rf_File, 128);
  2031.       animate_file (resultstring, *opt);
  2032.     }
  2033.   }
  2034. }
  2035.  
  2036. /****************************************************************************/
  2037.  
  2038. static LONG argarray[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  2039.  
  2040. int main (int argc, char *argv[])
  2041. {
  2042.   struct WBStartup *argmsg;
  2043.   struct WBArg *wb_arg;
  2044.   char **fnames;
  2045.   UWORD ktr;
  2046.   struct options opt;
  2047.  
  2048.   if (atexit (cleanup) != 0)
  2049.     die ("Can't install exit handler");
  2050.  
  2051.   GetProgramName (programname, 19);
  2052.  
  2053.   CyberGfxBase = OpenLibrary ("cybergraphics.library", 0);
  2054.  
  2055.   if ((IFFParseBase = OpenLibrary ("iffparse.library", 0)) == NULL)
  2056.     die ("Can't open iff.library");
  2057.  
  2058.   if ((AslBase = (struct Library *)OpenLibrary ("asl.library", 38L)) == NULL)
  2059.     die ("Can't open asl.library v38");
  2060.  
  2061.   if ((fr = (struct FileRequester *)AllocAslRequestTags (ASL_FileRequest,
  2062.            ASL_Hail,           (ULONG)programname,
  2063.            ASL_Pattern,        (ULONG)"~(#?.info)",
  2064.            ASL_OKText,         (ULONG)"Play",
  2065.            ASL_CancelText,     (ULONG)"Cancel",
  2066.            ASL_FuncFlags,      FILF_PATGAD,
  2067.            TAG_DONE)) == NULL)
  2068.     die ("Can't allocate file requester");
  2069.  
  2070.   if ((smr = AllocAslRequestTags (ASL_ScreenModeRequest, TAG_DONE)) == NULL)
  2071.     die ("Can't allocate screenmode requester");
  2072.  
  2073.   /* timer stuff */
  2074.   if ((timermp = CreatePort (NULL, 0)) == NULL)
  2075.     die ("Can't create messageport!");
  2076.   if ((timerio = (struct timerequest *)CreateExtIO (timermp,
  2077.                   sizeof(struct timerequest))) == NULL)
  2078.     die ("Can't create External IO!");
  2079.   if (timerclosed = OpenDevice (TIMERNAME, UNIT_ECLOCK,
  2080.                                 (struct IORequest *)timerio, 0))
  2081.     die ("Can't open timer.device!");
  2082.   TimerBase = (struct Library *)timerio->tr_node.io_Device;
  2083.   if ((time = (struct EClockVal *)AllocMem (sizeof(struct EClockVal),
  2084.                                           MEMF_CLEAR | MEMF_PUBLIC)) == NULL ||
  2085.       (time0 = (struct EClockVal *)AllocMem (sizeof(struct EClockVal),
  2086.                                           MEMF_CLEAR | MEMF_PUBLIC)) == NULL ||
  2087.       (time1 = (struct EClockVal *)AllocMem (sizeof(struct EClockVal),
  2088.                                           MEMF_CLEAR | MEMF_PUBLIC)) == NULL)
  2089.     die ("Out of memory");
  2090.   micros_per_eclock = 1000000.0 / (double)ReadEClock (time);
  2091.  
  2092.   /* more timer stuff */
  2093.   if ((timermp2 = CreatePort (NULL, 0)) == NULL)
  2094.     die ("Can't create messageport!");
  2095.   if ((timerio2 = (struct timerequest *)CreateExtIO (timermp2,
  2096.                    sizeof(struct timerequest))) == NULL)
  2097.     die ("Can't create External IO!");
  2098.   if (timer2closed = OpenDevice (TIMERNAME, UNIT_VBLANK,
  2099.                                  (struct IORequest *)timerio2, 0))
  2100.     die ("Can't open timer.device!");
  2101.  
  2102.   opt.ram = TRUE;
  2103.   opt.once = FALSE;
  2104.   opt.dbuf = TRUE;
  2105.   opt.warp = FALSE;
  2106.   opt.modereq = TRUE;
  2107.   opt.waittof = FALSE;
  2108.   opt.waitbovp = FALSE;
  2109.   opt.changescreenbuffer = FALSE;
  2110.   opt.loopframes = TRUE;
  2111.   opt.mode = INVALID_ID;
  2112.  
  2113.   /* parse workbench message or commandline */
  2114.   if (argc == 0) {
  2115.     argmsg = (struct WBStartup *)argv;
  2116.     wb_arg = argmsg->sm_ArgList;
  2117.     strcpy (programname, wb_arg->wa_Name);
  2118.     parse_tooltypes (wb_arg->wa_Name, &opt);
  2119.     if (argmsg->sm_NumArgs <= 1)
  2120.       filerequestloop (&opt);
  2121.     else {
  2122.       wb_arg++;
  2123.       for (ktr = 1; ktr < argmsg->sm_NumArgs; ktr++, wb_arg++)
  2124.         if (wb_arg->wa_Lock != NULL) {
  2125.           olddir = CurrentDir (wb_arg->wa_Lock);
  2126.           animate_file (wb_arg->wa_Name, opt);
  2127.           CurrentDir (olddir);
  2128.           olddir = NULL;
  2129.         } else
  2130.           animate_file (wb_arg->wa_Name, opt);
  2131.       }
  2132.   } else {
  2133.     if ((rdargs = ReadArgs
  2134.         ("FILE/M,DISK/S,RAM/S,ONCE/S,WARP/S,NOMODEREQ/S,WAITTOF/S,WAITBOVP/S,CH=CHANGESCREENBUFFER/S,NOLOOPFRAMES/S,SC=SCREENMODE/K",
  2135.          argarray, NULL)) != NULL) {
  2136.       chkabort ();
  2137.       if (argarray[1])
  2138.         opt.ram = FALSE;
  2139.       if (argarray[2])
  2140.         opt.ram = TRUE;
  2141.       if (argarray[3])
  2142.         opt.once = TRUE;
  2143.       if (argarray[4])
  2144.         opt.warp = TRUE;
  2145.       if (argarray[5])
  2146.         opt.modereq = FALSE;
  2147.       if (argarray[6])
  2148.         opt.waittof = TRUE;
  2149.       if (argarray[7])
  2150.         opt.waitbovp = TRUE;
  2151.       if (argarray[8])
  2152.         opt.changescreenbuffer = TRUE;
  2153.       if (argarray[9])
  2154.         opt.loopframes = FALSE;
  2155.       if (argarray[10] != NULL)
  2156.         opt.mode = parse_mode ((char *)argarray[10]);
  2157.       fnames = (char **)argarray[0];
  2158.       if (fnames == NULL || *fnames == NULL)
  2159.         filerequestloop (&opt);
  2160.       else
  2161.         while (*fnames != NULL)
  2162.           animate_file (*fnames++, opt);
  2163.       FreeArgs (rdargs);
  2164.       rdargs = NULL;
  2165.     }
  2166.   }
  2167.  
  2168.   return (0);
  2169. }
  2170.  
  2171. /****************************************************************************/
  2172.